# encoding:utf-8

"""
wechat channel
"""
import io
import json
import threading
import time

import requests

from bridge.context import *
from bridge.reply import *
from channel.chat_channel import ChatChannel
from channel.wechat.wechat_message import *
from common.mysql.MySQLhelper import MySQLhelper
from common.mysql.entity.module_info import SheQunModuleInfo
from common.mysql.entity.user_name_info import SheQunUserNameInfo
from common.mysql.shequn_accept_message_info import save_accept_message_info
from common.mysql.shequn_area_module_find import get_mysql_area_module_find_by_module_id
from common.mysql.shequn_container import get_shequn_container
from common.mysql.shequn_event_manager import get_event_manager_list, update_event_manager
from common.mysql.shequn_key_word import get_all_key_word
from common.mysql.shequn_key_word_find import insert_key_word_find
from common.mysql.shequn_key_word_house import get_shequn_key_word_house
from common.mysql.shequn_message_catch import get_shequn_message_catch_by_user_id, get_module_id_by_user_id
from common.mysql.shequn_qr_code_mysql import insert_she_qun_qr_code, get_max_order_in_qr_code
from common.mysql.shequn_voice import insert_shequn_voice

from common.user_data_util import *
from common.mysql.shequn_user_mysql import *

from common.expired_dict import ExpiredDict
from common.log import logger
from common.singleton import singleton
from common.time_check import time_checker
from config import conf, get_appdata_dir
from lib import itchat
from lib.itchat.content import *

import shortuuid
import pytz

sqlhelper = get_sql_helper()


@itchat.msg_register([TEXT, VOICE, PICTURE, NOTE, ATTACHMENT, SHARING])
def handler_single_msg(msg):
    try:
        cmsg = WechatMessage(msg, False)
    except NotImplementedError as e:
        logger.debug("[WX]single message {} skipped: {}".format(msg["MsgId"], e))
        return None
    message(False, cmsg)
    WechatChannel().handle_single(cmsg)
    return None


@itchat.msg_register([TEXT, VOICE, PICTURE, NOTE, ATTACHMENT, SHARING], isGroupChat=True)
def handler_group_msg(msg):
    try:
        cmsg = WechatMessage(msg, True)
    except NotImplementedError as e:
        logger.debug("[WX]group message {} skipped: {}".format(msg["MsgId"], e))
        return None
    message(True, cmsg)
    WechatChannel().handle_group(cmsg)
    return None


def get_user_info(cmsg):
    user_id = itchat.instance.storageClass.userName
    user_name = itchat.instance.storageClass.nickName
    friend_id = cmsg.to_user_id
    friend_name = cmsg.to_user_nickname
    if friend_id == user_id:
        friend_id = cmsg.from_user_id
        friend_name = cmsg.from_user_nickname
    type = cmsg.ctype
    mul_ori = get_mul_ori(user_id)
    # if mul_ori == None:
    #     logger.error("插入新群组失败！")
    return user_id, user_name, friend_id, friend_name, type, get_today(), mul_ori


# 进行消息匹配
def check_rule(user_id, flag):
    msg_type = get_shequn_message_catch_by_user_id(user_id)
    if msg_type == ():
        return False
    message_type = msg_type[0][0]
    if (message_type == 3) or (message_type == 2 and flag) or (message_type == 1 and not flag):
        return True
    return False


def check_module(user_id, friend_id):
    # 获取模块ID，确保调用时使用参数化查询
    module_id_value = get_module_id_by_user_id(user_id)
    if not module_id_value:
        return None

    module_id = module_id_value[0][0]

    if module_id == 'all':
        return 'all'

    # 获取关键字，确保调用时考虑异常处理和参数化查询
    try:
        results = get_mysql_area_module_find_by_module_id(module_id)
    except Exception as e:
        logger.error(f"Error fetching keywords: {e}")
        return None

    user_info = get_wechat_user_by_nick_id(friend_id)
    if not user_info:
        return None

    user_name_info = SheQunUserNameInfo(*user_info[0])

    name_properties = {
        user_name_info.extra_name,
        user_name_info.remark_name,
        user_name_info.friend_nick_name
    }

    # 优化关键词匹配和过滤逻辑
    return match_and_filter_keywords(results, name_properties)


def match_and_filter_keywords(results, name_properties):
    """
    匹配并过滤关键词。
    :param results: 关键词结果集
    :param name_properties: 用户名属性集合
    :return: 过滤后的关键词列表
    """
    matched_keywords = []
    excluded_keywords = []

    for row in results:
        module_info = SheQunModuleInfo(*row)
        # word = module_info.key_word
        # area_ids = module_info.area_id
        if module_info.key_word_lable == 1:
            matched_keywords.append(module_info)
        else:
            excluded_keywords.append(module_info)

    flag = False
    area_id = None
    for filter_keyword in matched_keywords:
        if flag:
            break
        for value in name_properties:
            if value != None:
                if filter_keyword.key_word in value:
                    flag = True
                    area_id = filter_keyword.area_id
                    break

    if flag:
        for filter_keyword in excluded_keywords:
            if not flag:
                return None
            for value in name_properties:
                if value != None:
                    if filter_keyword.key_word in value:
                        return None
    return area_id


def find_key_word(accept_message_info_id, cmsg, today, user_id, user_name, area_id):
    # 消息匹配
    key_word_all = get_all_key_word()
    if key_word_all != ():
        for key_words_value in key_word_all:
            key_id = key_words_value[0]
            key_word = key_words_value[1]
            key_word_house_id = key_words_value[2]
            if (key_word in cmsg.content):
                # 通过关键字查找关键字库
                key_word_House = get_shequn_key_word_house(key_word_house_id)
                key_word_house_name = None
                category_id = None
                category_name = None
                if key_word_House != ():
                    key_word_house_name = key_word_House[0][1]
                    category_id = key_word_House[0][2]
                    category_name = key_word_House[0][3]
                insert_key_word_find(accept_message_info_id, category_id, category_name, cmsg, key_id, key_word,
                                     key_word_house_id, key_word_house_name, today, user_id, user_name, area_id)


def update_or_save_wechat_user(today, user_id, user_name, friend_id, friend_name, flag, mul_ori, cmsg):
    value = get_wechat_user_by_friend(friend_id)
    if value != ():
        value = value[0]
        if value != None:
            id = value[0]
            friend_nick_name = value[1]
            if id != None:
                if (friend_nick_name == friend_name):
                    return
                else:
                    logger.info(
                        "已有群组注入，修改微信名 {} 的朋友微信号 {} 为 新的微信名: {}".format(user_name,
                                                                                              friend_nick_name,
                                                                                              friend_name))
                    update_wechat_user_by_fiend_id(friend_name, today, friend_id)

    else:
        # 新群组注入
        logger.info("新群组注入，修改微信名 {} 的朋友微信号 {} 为 {}".format(user_name, friend_name, friend_id))
        insert_new_group_wechat_user(today, user_name, user_id, friend_name,
                                     friend_id,
                                     flag, mul_ori, cmsg)


def get_mul_ori(user_id):
    value = get_mul_ori_by_user_id(user_id)
    if value != ():
        value = value[0]
        return value[0]
    else:
        return None


# def save_session_word_and_find_key_word(accept_message_info_id, cmsg, user_id, now_time, user_name):
#     # 如果用户的id和朋友的id已经在咨询中存在，查看最晚发送消息的时间
#     consultation_value = sqlhelper.fetch_all(
#         "select id, key_word, wechat_history_length, msg_actual_user_id, create_time from shequn_consultation_manager where user_id = %s and from_user_id = %s and msg_actual_user_id = %s order by create_time desc limit 1; ",
#         (user_id, cmsg.from_user_id, cmsg.actual_user_id))
#     # 在JAVA代码中进行定时任务来扫描该数据库，如果存在没有发送的消息，就进行发送给fastgpt
#     if (consultation_value != ()):
#         consultation_value = consultation_value[0]
#         # # 如果时间没有超过一个小时，那么会话没有结束，不进行关键字匹配
#         hour = get_hour(consultation_value)
#         if (hour < 1):
#             user_session_word_id = str(shortuuid.ShortUUID().random(20))
#             sqlhelper.insert_one(
#                 "INSERT INTO shequn_user_session_word (id, create_time, user_id, user_name, from_user_id, from_user_name, key_word, content, msg_actual_user_id, msg_actual_user_nickname, state, consultation_manager_id, is_group) VALUES(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s); ",
#                 (user_session_word_id, now_time, user_id, user_name, cmsg.from_user_id, cmsg.from_user_nickname,
#                  consultation_value[1], cmsg.content, cmsg.actual_user_id, cmsg.actual_user_nickname, 1,
#                  consultation_value[0], cmsg.is_group))
#             sqlhelper.update(
#                 "UPDATE shequn_consultation_manager SET wechat_history_length = %s WHERE id = %s; ",
#                 (consultation_value[2] + 1, consultation_value[0]))
#         else:
#             # 如果时间超过一个小时，那么会话视为结束，正常匹配
#             find_key_word(accept_message_info_id, cmsg, now_time, user_id, user_name)
#     else:
#         # 关键字匹配
#         find_key_word(accept_message_info_id, cmsg, now_time, user_id, user_name)


def get_hour(consultation_value):
    today = get_today()
    today = datetime.strptime(today, '%Y/%m/%d %H:%M:%S')
    today = today.strftime('%Y-%m-%d %H:%M:%S')

    create_time = consultation_value[4]
    create_time = create_time.strftime('%Y-%m-%d %H:%M:%S')
    date1 = datetime.strptime(today, "%Y-%m-%d %H:%M:%S")
    date2 = datetime.strptime(create_time, "%Y-%m-%d %H:%M:%S")
    limit_date = date1 - date2
    # 时间修改
    # hour = limit_date.seconds / 3600
    hour = limit_date.seconds
    return hour


def check_single_app(user_id, apply_code, chat_type=1):
    service_manager_count = sqlhelper.fetch_all(
        "select count(id) from shequn_service_manager where user_id = %s and apply_code = %s and chat_type = %s; ",
        (user_id, apply_code, chat_type))
    # if service_manager_count != ():
    #     value = service_manager_count[0]
    #     if value != None:
    #         num = value[0]
    #         if num > 0:
    #             return True
    # return False
    return service_manager_count and service_manager_count[0] and service_manager_count[0][0] > 0


def message(flag, cmsg):
    # 获取user_id和friend_id
    user_id, user_name, friend_id, friend_name, type, today, mul_ori = get_user_info(cmsg)
    # 修改微信用户信息
    update_or_save_wechat_user(today, user_id, user_name, friend_id, friend_name, flag, mul_ori, cmsg)
    # 保存接收到的信息
    accept_message_info_id = str(shortuuid.ShortUUID().random(20))
    save_accept_message_info(accept_message_info_id, cmsg, today, user_id, type, mul_ori, user_name)
    # 如果消息是图片，查看五分钟内是否有事件
    if type.value == 3:
        formatted_time = get_five_minutes_ago()
        event_list = get_event_manager_list(user_id, cmsg.from_user_id, cmsg.actual_user_id, today, formatted_time)
        if event_list != ():
            for index in event_list:
                update_event_manager(index[0], cmsg.content)
    # 判断是否有资格进入会话
    area_id = check_module(user_id, friend_id)
    if check_rule(user_id, flag) and (area_id == 'all' or area_id != None):
        if type.value == 2:
            insert_shequn_voice(accept_message_info_id, cmsg.content)
        chat_type = 0
        if flag:
            chat_type = 2
        else:
            chat_type = 1
        # 图片管家
        if check_single_app(user_id, 4, chat_type):
            # 如果是图片管家，那么将图片直接发送给图片处理模型
            if type.value == 3:
                insert_image(today, user_id, user_name, cmsg, accept_message_info_id, type, area_id)
                return
        # 所有数据都会进入到内部管家中
        # 是否为内部管家
        if check_single_app(user_id, 3):
            # 说明有数据，那么需要将该用户的所有好友的消息直接将发给fastgpt
            insert_vip_fastgpt(today, user_id, user_name, cmsg, accept_message_info_id, type)
            # 内部管家的数据直接插入到数据库，不用进行关键字匹配
            return
        if not flag:
            insert_key_word_find(accept_message_info_id, None, None, cmsg, None, None, None, None, today, user_id,
                                 user_name, area_id)
            return
        # 如果是@当前登陆人，那么将数据直接注入到关键字中
        if cmsg.content.find('@' + user_name) != -1:
            # 插入关键字
            insert_key_word_find(accept_message_info_id, None, None, cmsg, None, None, None, None, today, user_id,
                                 user_name,area_id, 1)
            return
        #  关键字匹配
        # save_session_word_and_find_key_word(accept_message_info_id, cmsg, user_id, today, user_name)
        find_key_word(accept_message_info_id, cmsg, today, user_id, user_name,area_id)


def insert_image(today, user_id, user_name, cmsg, accept_message_info_id, type, area_id):
    match_id = str(shortuuid.ShortUUID().random(20))
    image_text = '以下为基层治理事件类型：店外经营、机动车乱停乱放、非机动车乱停乱放、垃圾暴露、垃圾箱满溢、私搭乱建、道路积水、人员聚集、违规倾倒垃圾、打架斗殴、违章建筑、交通拥堵、土堆裸露、明火及烟雾。请分析并识别这张图片中的场景，判断其是否属于上述基层治理类型之一。如果是，请生成一个简洁明了的描述，如“路上有非机动车占道”，不需要给出详细描述。这个描述将直接用于数据库中的事件记录，请确保它简介、准确且具体，不包含其他与事件无关的话语。最后给出事件类别。'
    sqlhelper.insert_one(
        "INSERT INTO shequn_image (id, create_time, image_url, imgae_text, type, from_user_name, user_id, to_user_id, nick_name, to_user_name, from_user_id, message_id, msg_actual_user_id, msg_actual_nick_name, is_group, area_id) "
        "VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s); ",
        (
            match_id, today, cmsg.content, image_text, type.value, cmsg.from_user_nickname, user_id, cmsg.to_user_id,
            user_name, cmsg.to_user_nickname, cmsg.from_user_id,
            accept_message_info_id, cmsg.actual_user_id, cmsg.actual_user_nickname, cmsg.is_group, area_id
        ))


def insert_vip_fastgpt(today, user_id, user_name, cmsg, accept_message_info_id, type):
    match_id = str(shortuuid.ShortUUID().random(20))
    seq = str(shortuuid.ShortUUID().random(20))
    sqlhelper.insert_one(
        "INSERT INTO shequn_vip_send_fastgpt (id, create_time, user_id, nick_name, from_user_id, from_user_name, to_user_id, to_user_name, content, "
        " state, message_id, type, seq) "
        "VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s); ",
        (
            match_id, today, user_id, user_name, cmsg.from_user_id,
            cmsg.from_user_nickname, cmsg.to_user_id, cmsg.to_user_nickname, cmsg.content, 0, accept_message_info_id,
            type.value, seq))


def _check(func):
    def wrapper(self, cmsg: ChatMessage):
        msgId = cmsg.msg_id
        if msgId in self.receivedMsgs:
            logger.info("Wechat message {} already received, ignore".format(msgId))
            return
        self.receivedMsgs[msgId] = True
        create_time = cmsg.create_time  # 消息时间戳
        if conf().get("hot_reload") == True and int(create_time) < int(time.time()) - 60:  # 跳过1分钟前的历史消息
            logger.debug("[WX]history message {} skipped".format(msgId))
            return
        if cmsg.my_msg and not cmsg.is_group:
            logger.debug("[WX]my message {} skipped".format(msgId))
            return
        return func(self, cmsg)

    return wrapper


# 可用的二维码生成接口
# https://api.qrserver.com/v1/create-qr-code/?size=400×400&data=https://www.abc.com
# https://api.isoyu.com/qr/?m=1&e=L&p=20&url=https://www.abc.com
def check_and_set():
    # 容器创建，所有一切内容都是最新的，所有内容全部初始化。这时候，容器ID就是最新创建的容器ID
    if (MySQLhelper.the_single_one == None):
        MySQLhelper.the_single_one = str(shortuuid.ShortUUID().random(20))
        # 从二维码表查找这个ID的数据，如果不存在，那么创建顺序是+1
    if (MySQLhelper.create_order == None):
        create_order_max = get_max_order_in_qr_code()
        if create_order_max != ():
            max_order = create_order_max[0][0]
            if max_order == None:
                max_order = 0
        else:
            max_order = 0
        MySQLhelper.create_order = max_order + 1
    if (MySQLhelper.container == None):
        # 根据时间顺序找到对应的容器ID
        MySQLhelper.container, MySQLhelper.container_type, MySQLhelper.container_create_by = get_shequn_container()


def qrCallback(uuid, status, qrcode):
    # logger.debug("qrCallback: {} {}".format(uuid,status))
    if status == "0":
        try:
            from PIL import Image
            img = Image.open(io.BytesIO(qrcode))
            _thread = threading.Thread(target=img.show, args=("QRCode",))
            _thread.setDaemon(True)
            _thread.start()
        except Exception as e:
            pass

        import qrcode

        url = f"https://login.weixin.qq.com/l/{uuid}"

        qr_api1 = "https://api.isoyu.com/qr/?m=1&e=L&p=20&url={}".format(url)
        qr_api2 = "https://api.qrserver.com/v1/create-qr-code/?size=400×400&data={}".format(url)
        qr_api3 = "https://api.pwmqr.com/qrcode/create/?url={}".format(url)
        qr_api4 = "https://my.tv.sohu.com/user/a/wvideo/getQRCode.do?text={}".format(url)

        check_and_set()
        # 如果存在，保持相同
        insert_she_qun_qr_code(qr_api1, qr_api2, qr_api3, uuid, MySQLhelper.the_single_one, MySQLhelper.create_order,
                               MySQLhelper.container, MySQLhelper.container_type, MySQLhelper.container_create_by)
        print("You can also scan QRCode in any website below:")
        print(qr_api3)
        print(qr_api4)
        print(qr_api2)
        print(qr_api1)

        qr = qrcode.QRCode(border=1)
        qr.add_data(url)
        qr.make(fit=True)
        qr.print_ascii(invert=True)


@singleton
class WechatChannel(ChatChannel):
    NOT_SUPPORT_REPLYTYPE = []

    def __init__(self):
        super().__init__()
        self.receivedMsgs = ExpiredDict(60 * 60)

    def startup(self):
        itchat.instance.receivingRetryCount = 600  # 修改断线超时时间
        # login by scan QRCode
        hotReload = conf().get("hot_reload", False)
        status_path = os.path.join(get_appdata_dir(), "itchat.pkl")
        itchat.auto_login(
            enableCmdQR=2,
            hotReload=hotReload,
            statusStorageDir=status_path,
            qrCallback=qrCallback,
        )
        self.user_id = itchat.instance.storageClass.userName
        self.name = itchat.instance.storageClass.nickName
        # logger.info("Wechat login success, uuid: {}, nickname: {}".format(uuid, self.name))
        # start message listener
        # sqlhelper.insert_one(
        #     "INSERT INTO weixin_info (weixinName, userId) VALUES(%s, %s); ",
        #     (self.name, self.user_id))
        itchat.run()

    # handle_* 系列函数处理收到的消息后构造Context，然后传入produce函数中处理Context和发送回复
    # Context包含了消息的所有信息，包括以下属性
    #   type 消息类型, 包括TEXT、VOICE、IMAGE_CREATE
    #   content 消息内容，如果是TEXT类型，content就是文本内容，如果是VOICE类型，content就是语音文件名，如果是IMAGE_CREATE类型，content就是图片生成命令
    #   kwargs 附加参数字典，包含以下的key：
    #        session_id: 会话id
    #        isgroup: 是否是群聊
    #        receiver: 需要回复的对象
    #        msg: ChatMessage消息对象
    #        origin_ctype: 原始消息类型，语音转文字后，私聊时如果匹配前缀失败，会根据初始消息是否是语音来放宽触发规则
    #        desire_rtype: 希望回复类型，默认是文本回复，设置为ReplyType.VOICE是语音回复

    @time_checker
    @_check
    def handle_single(self, cmsg: ChatMessage):
        # filter system message
        if cmsg.other_user_id in ["weixin"]:
            return
        if cmsg.ctype == ContextType.VOICE:
            if conf().get("speech_recognition") != True:
                return
            logger.debug("[WX]receive voice msg: {}".format(cmsg.content))
        elif cmsg.ctype == ContextType.IMAGE:
            logger.debug("[WX]receive image msg: {}".format(cmsg.content))
        elif cmsg.ctype == ContextType.PATPAT:
            logger.debug("[WX]receive patpat msg: {}".format(cmsg.content))
        elif cmsg.ctype == ContextType.TEXT:
            logger.debug("[WX]receive text msg: {}, cmsg={}".format(json.dumps(cmsg._rawmsg, ensure_ascii=False), cmsg))
        else:
            logger.debug("[WX]receive msg: {}, cmsg={}".format(cmsg.content, cmsg))
        context = self._compose_context(cmsg.ctype, cmsg.content, isgroup=False, msg=cmsg)
        if context:
            self.produce(context)

    @time_checker
    @_check
    def handle_group(self, cmsg: ChatMessage):
        if cmsg.ctype == ContextType.VOICE:
            if conf().get("group_speech_recognition") != True:
                return
            logger.debug("[WX]receive voice for group msg: {}".format(cmsg.content))
        elif cmsg.ctype == ContextType.IMAGE:
            logger.debug("[WX]receive image for group msg: {}".format(cmsg.content))
        elif cmsg.ctype in [ContextType.JOIN_GROUP, ContextType.PATPAT, ContextType.ACCEPT_FRIEND]:
            logger.debug("[WX]receive note msg: {}".format(cmsg.content))
        elif cmsg.ctype == ContextType.TEXT:
            pass
        elif cmsg.ctype == ContextType.FILE:
            logger.debug(f"[WX]receive attachment msg, file_name={cmsg.content}")
        else:
            logger.debug("[WX]receive group msg: {}".format(cmsg.content))
        context = self._compose_context(cmsg.ctype, cmsg.content, isgroup=True, msg=cmsg)
        if context:
            self.produce(context)

    # 统一的发送函数，每个Channel自行实现，根据reply的type字段发送不同类型的消息
    def send(self, reply: Reply, context: Context):
        receiver = context["receiver"]
        if reply.type == ReplyType.TEXT:
            itchat.send(reply.content, toUserName=receiver)
            logger.info("[WX] sendMsg={}, receiver={}".format(reply, receiver))
        elif reply.type == ReplyType.ERROR or reply.type == ReplyType.INFO:
            itchat.send(reply.content, toUserName=receiver)
            logger.info("[WX] sendMsg={}, receiver={}".format(reply, receiver))
        elif reply.type == ReplyType.VOICE:
            itchat.send_file(reply.content, toUserName=receiver)
            logger.info("[WX] sendFile={}, receiver={}".format(reply.content, receiver))
        elif reply.type == ReplyType.IMAGE_URL:  # 从网络下载图片
            img_url = reply.content
            logger.debug(f"[WX] start download image, img_url={img_url}")
            pic_res = requests.get(img_url, stream=True)
            image_storage = io.BytesIO()
            size = 0
            for block in pic_res.iter_content(1024):
                size += len(block)
                image_storage.write(block)
            logger.info(f"[WX] download image success, size={size}, img_url={img_url}")
            image_storage.seek(0)
            itchat.send_image(image_storage, toUserName=receiver)
            logger.info("[WX] sendImage url={}, receiver={}".format(img_url, receiver))
        elif reply.type == ReplyType.IMAGE:  # 从文件读取图片
            image_storage = reply.content
            image_storage.seek(0)
            itchat.send_image(image_storage, toUserName=receiver)
            logger.info("[WX] sendImage, receiver={}".format(receiver))
        elif reply.type == ReplyType.FILE:  # 新增文件回复类型
            file_storage = reply.content
            itchat.send_file(file_storage, toUserName=receiver)
            logger.info("[WX] sendFile, receiver={}".format(receiver))
        elif reply.type == ReplyType.VIDEO:  # 新增视频回复类型
            video_storage = reply.content
            itchat.send_video(video_storage, toUserName=receiver)
            logger.info("[WX] sendFile, receiver={}".format(receiver))
        elif reply.type == ReplyType.VIDEO_URL:  # 新增视频URL回复类型
            video_url = reply.content
            logger.debug(f"[WX] start download video, video_url={video_url}")
            video_res = requests.get(video_url, stream=True)
            video_storage = io.BytesIO()
            size = 0
            for block in video_res.iter_content(1024):
                size += len(block)
                video_storage.write(block)
            logger.info(f"[WX] download video success, size={size}, video_url={video_url}")
            video_storage.seek(0)
            itchat.send_video(video_storage, toUserName=receiver)
            logger.info("[WX] sendVideo url={}, receiver={}".format(video_url, receiver))
